]> dgit.raspbian.org Git - gnome-shell.git/commitdiff
keyboard: Add KeyboardManager to manage the lifetime of the keyboard actor
authorMarco Trevisan (Treviño) <mail@3v1n0.net>
Wed, 28 Aug 2019 21:52:52 +0000 (23:52 +0200)
committerFlorian Müllner <florian.muellner@gmail.com>
Wed, 16 Oct 2019 15:26:12 +0000 (15:26 +0000)
The Keyboard class used to be both a view and controller class, however in order
to make the keyboard a native Clutter.Actor, we need to separate the widget from
the controller class, so that we can manage the actor lifetime from the JS side.

Thus, initialize the keyboard actor on the Keyboard constructor and create a
KeyboardManager class to manage its state and lifetime.

Add proxy methods for the public functions that were used by other shell
components

https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/559

js/ui/keyboard.js
js/ui/main.js

index b6aa58fcb637c011dc63845b2d62843b80d4b4e8..109c0a770eae0d025d96d3103995e7b4d0d7013a 100644 (file)
@@ -1,5 +1,5 @@
 // -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
-/* exported Keyboard */
+/* exported KeyboardManager */
 
 const { Clutter, Gio, GLib, GObject, Meta, St } = imports.gi;
 const Signals = imports.signals;
@@ -1042,9 +1042,95 @@ var Keypad = class Keypad {
 };
 Signals.addSignalMethods(Keypad.prototype);
 
+var KeyboardManager = class KeyBoardManager {
+    constructor() {
+        this._keyboard = null;
+        this._a11yApplicationsSettings = new Gio.Settings({ schema_id: A11Y_APPLICATIONS_SCHEMA });
+        this._a11yApplicationsSettings.connect('changed', this._syncEnabled.bind(this));
+
+        this._lastDeviceId = null;
+        Meta.get_backend().connect('last-device-changed', (backend, deviceId) => {
+            let manager = Clutter.DeviceManager.get_default();
+            let device = manager.get_device(deviceId);
+
+            if (device.get_device_name().indexOf('XTEST') < 0) {
+                this._lastDeviceId = deviceId;
+                this._syncEnabled();
+            }
+        });
+        this._syncEnabled();
+    }
+
+    _lastDeviceIsTouchscreen() {
+        if (!this._lastDeviceId)
+            return false;
+
+        let manager = Clutter.DeviceManager.get_default();
+        let device = manager.get_device(this._lastDeviceId);
+
+        if (!device)
+            return false;
+
+        return device.get_device_type() == Clutter.InputDeviceType.TOUCHSCREEN_DEVICE;
+    }
+
+    _syncEnabled() {
+        let enableKeyboard = this._a11yApplicationsSettings.get_boolean(SHOW_KEYBOARD);
+        let enabled = enableKeyboard || this._lastDeviceIsTouchscreen();
+        if (!enabled && !this._keyboard)
+            return;
+
+        if (enabled && !this._keyboard) {
+            this._keyboard = new Keyboard();
+        } else if (!enabled && this._keyboard) {
+            this._keyboard.setCursorLocation(null);
+            Main.layoutManager.hideKeyboard(true);
+            this._keyboard.destroy();
+            this._keyboard = null;
+        }
+    }
+
+    get keyboardActor() {
+        return this._keyboard.actor;
+    }
+
+    get visible() {
+        return this._keyboard && this._keyboard.actor.visible;
+    }
+
+    open(monitor) {
+        if (this._keyboard)
+            this._keyboard.open(monitor);
+    }
+
+    close() {
+        if (this._keyboard)
+            this._keyboard.close();
+    }
+
+    addSuggestion(text, callback) {
+        if (this._keyboard)
+            this._keyboard.addSuggestion(text, callback);
+    }
+
+    resetSuggestions() {
+        if (this._keyboard)
+            this._keyboard.resetSuggestions();
+    }
+
+    shouldTakeEvent(event) {
+        if (!this._keyboard)
+            return false;
+
+        let actor = event.get_source();
+        return Main.layoutManager.keyboardBox.contains(actor) ||
+               !!actor._extended_keys || !!actor.extended_key;
+    }
+};
+
 var Keyboard = class Keyboard {
     constructor() {
-        this.actor = null;
+        this.actor = new St.BoxLayout({ name: 'keyboard', vertical: true });
         this._focusInExtendedKeys = false;
         this._emojiActive = false;
 
@@ -1053,113 +1139,82 @@ var Keyboard = class Keyboard {
         this._animFocusedWindow = null;
         this._delayedAnimFocusWindow = null;
 
-        this._enableKeyboard = false; // a11y settings value
-        this._enabled = false; // enabled state (by setting or device type)
         this._latched = false; // current level is latched
 
-        this._a11yApplicationsSettings = new Gio.Settings({ schema_id: A11Y_APPLICATIONS_SCHEMA });
-        this._a11yApplicationsSettings.connect('changed', this._syncEnabled.bind(this));
-        this._lastDeviceId = null;
         this._suggestions = null;
         this._emojiKeyVisible = Meta.is_wayland_compositor();
 
         this._focusTracker = new FocusTracker();
-        this._focusTracker.connect('position-changed', this._onFocusPositionChanged.bind(this));
-        this._focusTracker.connect('reset', () => {
+        this._connectSignal(this._focusTracker, 'position-changed',
+            this._onFocusPositionChanged.bind(this));
+        this._connectSignal(this._focusTracker, 'reset', () => {
             this._delayedAnimFocusWindow = null;
             this._animFocusedWindow = null;
             this._oskFocusWindow = null;
         });
-        this._focusTracker.connect('focus-changed', (tracker, focused) => {
-            // Valid only for X11
-            if (Meta.is_wayland_compositor())
-                return;
-
-            if (focused)
-                this.open(Main.layoutManager.focusIndex);
-            else
-                this.close();
-        });
-
-        Meta.get_backend().connect('last-device-changed',
-            (backend, deviceId) => {
-                let manager = Clutter.DeviceManager.get_default();
-                let device = manager.get_device(deviceId);
-
-                if (!device.get_device_name().includes('XTEST')) {
-                    this._lastDeviceId = deviceId;
-                    this._syncEnabled();
-                }
+        // Valid only for X11
+        if (!Meta.is_wayland_compositor()) {
+            this._connectSignal(this._focusTracker, 'focus-changed', (_tracker, focused) => {
+                if (focused)
+                    this.open(Main.layoutManager.focusIndex);
+                else
+                    this.close();
             });
-        this._syncEnabled();
+        }
 
         this._showIdleId = 0;
 
         this._keyboardVisible = false;
-        Main.layoutManager.connect('keyboard-visible-changed', (o, visible) => {
+        this._connectSignal(Main.layoutManager, 'keyboard-visible-changed', (_lm, visible) => {
             this._keyboardVisible = visible;
         });
         this._keyboardRequested = false;
         this._keyboardRestingId = 0;
 
-        Main.layoutManager.connect('monitors-changed', this._relayout.bind(this));
-    }
+        this._connectSignal(Main.layoutManager, 'monitors-changed', this._relayout.bind(this));
 
-    get visible() {
-        return this._keyboardVisible;
+        this._setupKeyboard();
+
+        this.actor.connect('destroy', this._onDestroy.bind(this));
     }
 
-    _onFocusPositionChanged(focusTracker) {
-        let rect = focusTracker.getCurrentRect();
-        this.setCursorLocation(focusTracker.currentWindow, rect.x, rect.y, rect.width, rect.height);
+    destroy() {
+        this.actor.destroy();
     }
 
-    _lastDeviceIsTouchscreen() {
-        if (!this._lastDeviceId)
-            return false;
+    _connectSignal(obj, signal, callback) {
+        if (!this._connectionsIDs)
+            this._connectionsIDs = [];
 
-        let manager = Clutter.DeviceManager.get_default();
-        let device = manager.get_device(this._lastDeviceId);
+        let id = obj.connect(signal, callback);
+        this._connectionsIDs.push([obj, id]);
+        return id;
+    }
 
-        if (!device)
-            return false;
+    get visible() {
+        return this._keyboardVisible && this.actor.visible;
+    }
 
-        return device.get_device_type() == Clutter.InputDeviceType.TOUCHSCREEN_DEVICE;
+    _onFocusPositionChanged(focusTracker) {
+        let rect = focusTracker.getCurrentRect();
+        this.setCursorLocation(focusTracker.currentWindow, rect.x, rect.y, rect.width, rect.height);
     }
 
-    _syncEnabled() {
-        let wasEnabled = this._enabled;
-        this._enableKeyboard = this._a11yApplicationsSettings.get_boolean(SHOW_KEYBOARD);
-        this._enabled = this._enableKeyboard || this._lastDeviceIsTouchscreen();
-        if (!this._enabled && !this._keyboardController)
-            return;
+    _onDestroy() {
+        for (let [obj, id] of this._connectionsIDs)
+            obj.disconnect(id);
+        delete this._connectionsIDs;
 
-        if (this._enabled && !this._keyboardController)
-            this._setupKeyboard();
-        else if (!this._enabled)
-            this.setCursorLocation(null);
+        this._clearShowIdle();
 
-        if (!this._enabled && wasEnabled)
-            Main.layoutManager.hideKeyboard(true);
-    }
+        this._keyboardController = null;
+        this._suggestions = null;
+        this._aspectContainer = null;
+        this._emojiSelection = null;
+        this._keypad = null;
 
-    _destroyKeyboard() {
-        if (this._keyboardNotifyId)
-            this._keyboardController.disconnect(this._keyboardNotifyId);
-        if (this._keyboardGroupsChangedId)
-            this._keyboardController.disconnect(this._keyboardGroupsChangedId);
-        if (this._keyboardStateId)
-            this._keyboardController.disconnect(this._keyboardStateId);
-        if (this._emojiKeyVisibleId)
-            this._keyboardController.disconnect(this._emojiKeyVisibleId);
-        if (this._keypadVisibleId)
-            this._keyboardController.disconnect(this._keypadVisibleId);
-        if (this._focusNotifyId)
-            global.stage.disconnect(this._focusNotifyId);
-        this._clearShowIdle();
-        this._keyboard = null;
-        this.actor.destroy();
-        this.actor = null;
+        Main.layoutManager.untrackChrome(this.actor);
+        Main.layoutManager.keyboardBox.remove_actor(this.actor);
 
         if (this._languagePopup) {
             this._languagePopup.destroy();
@@ -1168,7 +1223,6 @@ var Keyboard = class Keyboard {
     }
 
     _setupKeyboard() {
-        this.actor = new St.BoxLayout({ name: 'keyboard', vertical: true, reactive: true });
         Main.layoutManager.keyboardBox.add_actor(this.actor);
         Main.layoutManager.trackChrome(this.actor);
 
@@ -1196,7 +1250,7 @@ var Keyboard = class Keyboard {
         this._emojiSelection.actor.hide();
 
         this._keypad = new Keypad();
-        this._keypad.connect('keyval', (keypad, keyval) => {
+        this._connectSignal(this._keypad, 'keyval', (_keypad, keyval) => {
             this._keyboardController.keyvalPress(keyval);
             this._keyboardController.keyvalRelease(keyval);
         });
@@ -1212,14 +1266,20 @@ var Keyboard = class Keyboard {
         // keyboard on RTL locales.
         this.actor.text_direction = Clutter.TextDirection.LTR;
 
-        this._keyboardNotifyId = this._keyboardController.connect('active-group', this._onGroupChanged.bind(this));
-        this._keyboardGroupsChangedId = this._keyboardController.connect('groups-changed', this._onKeyboardGroupsChanged.bind(this));
-        this._keyboardStateId = this._keyboardController.connect('panel-state', this._onKeyboardStateChanged.bind(this));
-        this._keypadVisibleId = this._keyboardController.connect('keypad-visible', this._onKeypadVisible.bind(this));
-        this._focusNotifyId = global.stage.connect('notify::key-focus', this._onKeyFocusChanged.bind(this));
+        this._connectSignal(this._keyboardController, 'active-group',
+            this._onGroupChanged.bind(this));
+        this._connectSignal(this._keyboardController, 'groups-changed',
+            this._onKeyboardGroupsChanged.bind(this));
+        this._connectSignal(this._keyboardController, 'panel-state',
+            this._onKeyboardStateChanged.bind(this));
+        this._connectSignal(this._keyboardController, 'keypad-visible',
+            this._onKeypadVisible.bind(this));
+        this._connectSignal(global.stage, 'notify::key-focus',
+            this._onKeyFocusChanged.bind(this));
 
         if (Meta.is_wayland_compositor())
-            this._emojiKeyVisibleId = this._keyboardController.connect('emoji-visible', this._onEmojiKeyVisible.bind(this));
+            this._connectSignal(this._keyboardController, 'emoji-visible',
+                this._onEmojiKeyVisible.bind(this));
 
         this._relayout();
     }
@@ -1245,7 +1305,7 @@ var Keyboard = class Keyboard {
                 this._showIdleId = 0;
                 return GLib.SOURCE_REMOVE;
             });
-            GLib.Source.set_name_by_id(this._showIdleId, '[gnome-shell] this.show');
+            GLib.Source.set_name_by_id(this._showIdleId, '[gnome-shell] this.open');
         }
     }
 
@@ -1552,12 +1612,6 @@ var Keyboard = class Keyboard {
         this._updateCurrentPageVisible();
     }
 
-    shouldTakeEvent(event) {
-        let actor = event.get_source();
-        return Main.layoutManager.keyboardBox.contains(actor) ||
-               !!actor._extended_keys || !!actor.extended_key;
-    }
-
     _clearKeyboardRestTimer() {
         if (!this._keyboardRestingId)
             return;
@@ -1566,9 +1620,6 @@ var Keyboard = class Keyboard {
     }
 
     open(monitor) {
-        if (!this._enabled)
-            return;
-
         this._clearShowIdle();
         this._keyboardRequested = true;
 
@@ -1608,9 +1659,6 @@ var Keyboard = class Keyboard {
     }
 
     close() {
-        if (!this._enabled)
-            return;
-
         this._clearShowIdle();
         this._keyboardRequested = false;
 
index ab5aff0dcbb2471754d1c3aad0676c15c543b10b..93122890ec6d7a3f614f2f2c9711153d046ca738 100644 (file)
@@ -189,7 +189,7 @@ function _initializeUI() {
 
     messageTray = new MessageTray.MessageTray();
     panel = new Panel.Panel();
-    keyboard = new Keyboard.Keyboard();
+    keyboard = new Keyboard.KeyboardManager();
     notificationDaemon = new NotificationDaemon.NotificationDaemon();
     windowAttentionHandler = new WindowAttentionHandler.WindowAttentionHandler();
     componentManager = new Components.ComponentManager();